﻿ using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.IO;

using Microsoft.VisualBasic;

using System.Net;
using System.Net.Sockets;
using System.Threading;

using Lists;
using Sim;
using Users;

namespace ServerApplication
{
    public class ServerApplications
    {
        ASCIIEncoding byteEncode = new ASCIIEncoding();
        private UdpClient udpTransmit = new UdpClient(25002);
        private UdpClient udpReceive = new UdpClient(25001);
        IPEndPoint rxPoint = new IPEndPoint(IPAddress.Any, 25001);
        IPEndPoint bcPoint = new IPEndPoint(IPAddress.Broadcast, 25002);
        private byte[] byteMsgReceive;
        private byte[] byteMsgSend;
        string serverIP = IPList.GetPublicIP();

        public void Broadcast()
        {
            while (true)
            {
                byteMsgSend = byteEncode.GetBytes(serverIP + "#ServerBroadcast");

                udpTransmit.Send(byteMsgSend, byteMsgSend.Length, bcPoint);
                Thread.Sleep(2000);
            }
        }

        public void refreshBroadcast()
        {
            while (true)
            {
                sendContestInformationBroadcast();
                Thread.Sleep(100);
                sendUserInformationBroadcast();
                Thread.Sleep(100);
                sendContestRegistrationBroadcast();
                Thread.Sleep(6000);
            }
        }

        public void Receive()
        {
            while (true)
            {
                byteMsgReceive = udpReceive.Receive(ref rxPoint);

                string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
                string funcCall = split[1];

                Console.WriteLine(byteEncode.GetString(byteMsgReceive));

                if (funcCall == "Login")
                {
                    Console.WriteLine("Trying to login");
                    receiveLogin();
                }
                else if (funcCall == "Login Success")
                {
                    sendContestInformation();
                    sendUserInformation();
                    Console.WriteLine("Sending contest information");
                }
                else if (funcCall == "SendJudgeScore")
                    receiveScore();
                else if (funcCall == "RegisterUser")
                    recieveUser();
                else if (funcCall == "SendDive")
                    recieveDive();
                else if (funcCall == "RefreshTournament")
                {
                    sendContestInformation();
                }
                else if (funcCall == "RefreshUser")
                {
                    sendUserInformation();
                }
                else if (funcCall == "Tournament")
                {
                    receiveTournament();
                }
                else if (funcCall == "ApplyForTournament")
                {
                    receiveTournamentRegistration();
                }
                else if (funcCall == "ApplicationAccepted")
                {
                    receiveConfirm();
                }
                else if (funcCall == "ApplicationDeclined")
                {
                    TournamentRegisterList.RemoveRegistration(split[2], split[3]);
                }
                else if (funcCall == "RemoveTournament")
                {
                    removeTournament();
                }
                else if (funcCall == "StartTournament")
                {
                    startTournament();
                }
                else if (funcCall == "AddJudge")
                {
                    recieveJudgeForTournament();
                }

            }
        }

        public void sendContestRegistration()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];

            for (int i = 0; i < TournamentRegisterList.tournamentRegistrationList.Count; i++)
            {
                Transmit("Server#RefreshTournamentRegister#" + TournamentRegisterList.tournamentRegistrationList[i].username + "#" +
                TournamentRegisterList.tournamentRegistrationList[i].tournamentName, ip);
            }
        }

        public void sendContestInformation()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];

            Transmit("Server#RefreshTournament#" + TournamentList.stringSerializeFromXML(), ip);
        }

        public void sendUserInformation()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];


            for (int i = 0; i < UserList.userList.Count; i++)
            {
                Transmit("Server#RefreshUser#" + UserList.userList[i].id.ToString() + "#" + UserList.userList[i].username + "#" + 
                UserList.userList[i].firstName + "#" + UserList.userList[i].lastName + "#"  + 
                UserList.userList[i].age.ToString() + "#" + UserList.userList[i].sex + "#" +
                UserList.userList[i].nation + "#" + UserList.userList[i].level.ToString(), ip);
            }
        }

        public int recieveDive()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];

            for (int i = 0; i < TournamentList.tournamentList.Count; i++)
            {
                if (TournamentList.tournamentList[i].title == split[3])
                {
                    for (int j = 0; j < TournamentList.tournamentList[i].diverList.Count; j++)
                    {
                        if (TournamentList.tournamentList[i].diverList[j].username == split[2])
                        {
                            for (int k = 4; k < 10; k++)
                            {
                                TournamentList.tournamentList[i].diverList[j].dives.Add(new Dive(split[k], TournamentList.tournamentList[i].jumpHeight));
                            }
                            TournamentList.SerializeToXML();
                            return 1;
                        }
                    }
                }
            }
            return 0;
        }


        public int recieveUser()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];
            string firstName = split[2];
            string lastName = split[3];
            int age = Convert.ToInt32(split[4]);
            string nation = split[5];
            string sex = split[6];
            int level = Convert.ToInt32(split[7]);
            string username = split[8];
            string password = split[9];
            User RegisterUser = new User(firstName, lastName, age, nation, sex, level, username, password);
            int Register = UserList.AddUser(RegisterUser);
            if (Register == 0)
                Transmit("Server#Registration#Failed", ip);
            else if(Register == 1)
                Transmit("Server#Registration#Success", ip);
            return 0;
        }
        public int receiveScore()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];

            for (int i = 0; i < TournamentList.tournamentList.Count; i++)
            {
                if(TournamentList.tournamentList[i].title == split[3])
                    for (int j = 0; j < TournamentList.tournamentList[i].diverList.Count; j++)
                    {
                        if (TournamentList.tournamentList[i].diverList[j].username == split[2])
                        {
                            for (int k = 0; k < TournamentList.tournamentList[i].diverList[j].dives[Convert.ToInt32(split[6])].diveScore.Count; k++)
                            {
                                if (TournamentList.tournamentList[i].diverList[j].dives[Convert.ToInt32(split[6])].diveScore[k].usernameOfJudge == split[7])
                                    return 0;
                            }
                            TournamentList.tournamentList[i].diverList[j].dives[Convert.ToInt32(split[6])].setJudgeScore(Convert.ToDouble(split[5]), split[7]);
                            if (TournamentList.tournamentList[i].judgeList.Count ==
                                TournamentList.tournamentList[i].diverList[j].dives[Convert.ToInt32(split[6])].diveScore.Count)
                            {
                                TournamentList.tournamentList[i].diverList[j].totalScore += TournamentList.tournamentList[i].diverList[j].dives[Convert.ToInt32(split[6])].CalcPtsnDD(
                                    TournamentList.tournamentList[i].diverList[j].dives[Convert.ToInt32(split[6])].diveCode, TournamentList.tournamentList[i].jumpHeight);
                            }
                            return 1;
                        }
                    }
            }
            return 0;
        }

        public int receiveLogin()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];

            if (LoginValidation(byteEncode.GetString(byteMsgReceive)) == 1)
            {
                bool isInTournament = false;
                string nation = "";
                string gender = "";
                string inTournament = "";
                int level = 0;
                for (int i = 0; i < UserList.userList.Count; i++)
			    {
			        if(split[2] == UserList.userList[i].username)
                    {
                        nation = UserList.userList[i].nation;
                        gender = UserList.userList[i].sex;
                        level = UserList.userList[i].level;
                        break;
                    }
			    }
                for (int i = 0; i < TournamentList.tournamentList.Count; i++)
                {
                    if (!TournamentList.tournamentList[i].isFinished)
                    {
                        for (int j = 0; j < TournamentList.tournamentList[i].diverList.Count; j++)
                        {
                            if (split[2] == TournamentList.tournamentList[i].diverList[j].username)
                            {
                                inTournament = TournamentList.tournamentList[i].title;
                                isInTournament = true;
                                break;
                            }
                        }
                        for (int j = 0; j < TournamentList.tournamentList[i].judgeList.Count; j++)
                        {
                            if (split[2] == TournamentList.tournamentList[i].judgeList[j].username)
                            {
                                inTournament = TournamentList.tournamentList[i].title;
                                isInTournament = true;
                                break;
                            }
                        }
                        if (isInTournament)
                            break;
                    }
                }
                IPList.AddIPUser(split[2], ip);
                Transmit("Server#Login#Success#" + split[2] + "#" + nation + "#" + gender + "#" + level.ToString() + "#" + inTournament, ip);
                return 1;
            }
            else
            {
                Transmit("Server#Login#Failed", ip);
                return 0;
            }
        }

        public void receiveTournament()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];
            Tournament RegisterTournament = new Tournament(split[2], split[3], Convert.ToInt32(split[4]), split[5], Convert.ToInt32(split[6]), Convert.ToInt32(split[7]));
            TournamentList.AddTournament(RegisterTournament);
        }

        public void receiveConfirm()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];

            for (int i = 0; i < TournamentList.tournamentList.Count; i++)
            {
                if (split[3] == TournamentList.tournamentList[i].title)
                {
                    TournamentList.tournamentList[i].AddDiver(split[2], i);
                    TournamentRegisterList.RemoveRegistration(split[2], split[3]);
                    break;
                }
            }
        }

        public void recieveJudgeForTournament()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];

            for (int i = 0; i < TournamentList.tournamentList.Count; i++)
            {
                if (split[3] == TournamentList.tournamentList[i].title)
                {
                    TournamentList.tournamentList[i].AddJudge(split[2], i);
                    break;
                }
            }
        }


        public void receiveTournamentRegistration()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];
            Registration RegisterForTournament = new Registration(split[2], split[3]);
            TournamentRegisterList.AddRegistration(RegisterForTournament);
        }

        public void Transmit(string msg, string ip)
        {
            string transmittingIP = ip;
            byteMsgSend = byteEncode.GetBytes(msg);
            Console.WriteLine(msg);
            try
            {
                udpTransmit.Send(byteMsgSend, byteMsgSend.Length, transmittingIP, 25002);
            }
            catch
            {
                
            }
        }

        int LoginValidation(string line)
        {
            string[] split = line.Split('#');
            string username = split[2];
            string password = split[3];
            Login login = new Login();
            return login.Validate(username, password);
        }

        public void sendContestInformationBroadcast()
        {

            byteMsgSend = byteEncode.GetBytes("Server#RefreshTournament#"+ TournamentList.stringSerializeFromXML());

            udpTransmit.Send(byteMsgSend, byteMsgSend.Length, bcPoint);

        }

        public void sendUserInformationBroadcast()
        {

            for (int i = 0; i < UserList.userList.Count; i++)
            {
                byteMsgSend = byteEncode.GetBytes("Server#RefreshUser#" + UserList.userList[i].id.ToString() + "#" + UserList.userList[i].username + "#" +
                UserList.userList[i].firstName + "#" + UserList.userList[i].lastName + "#" +
                UserList.userList[i].age.ToString() + "#" + UserList.userList[i].sex + "#" +
                UserList.userList[i].nation + "#" + UserList.userList[i].level.ToString());
                udpTransmit.Send(byteMsgSend, byteMsgSend.Length, bcPoint);
            }
        }

        public void sendContestRegistrationBroadcast()
        {

            for (int i = 0; i < TournamentRegisterList.tournamentRegistrationList.Count; i++)
            {
                byteMsgSend = byteEncode.GetBytes("Server#RefreshTournamentRegister#" + TournamentRegisterList.tournamentRegistrationList[i].username + "#" +
                TournamentRegisterList.tournamentRegistrationList[i].tournamentName);
                udpTransmit.Send(byteMsgSend, byteMsgSend.Length, bcPoint);
            }
        }

        public void removeTournament()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];
            TournamentList.RemoveTournament(split[2]);
            Program.mtp.KillThread(split[2]);
        }


        public void startTournament()
        {
            string[] split = byteEncode.GetString(byteMsgReceive).Split('#');
            string ip = split[0];
            for (int i = 0; i < TournamentList.tournamentList.Count; i++)
            {
                if (TournamentList.tournamentList[i].isStarted == true)
                    return;
            }
            for (int j = 0; j < TournamentList.tournamentList.Count; j++)
            {
                if (TournamentList.tournamentList[j].title == split[2])
                {
                    TournamentList.tournamentList[j].isStarted = true;
                    TournamentList.SerializeToXML();
                    Program.mtp.LaunchThread(TournamentList.tournamentList[j].title);
                    break;
                }
            }
            
        }

        public void checkNextRound(object tournamentName)
        {
            for (int i = 0; i < TournamentList.tournamentList.Count; i++)
            {
                if ((string)tournamentName == TournamentList.tournamentList[i].title)
                {
                    bool nextRound = false;
                    int round = 0;
                    int counter = 0;
                    while (TournamentList.tournamentList[i].isStarted && !TournamentList.tournamentList[i].isFinished)
                    {
                        Console.WriteLine("Counter = " + counter + ", Tournament = " + i + ", Round = " + round);
                         
                            for (int judges = 0; judges < TournamentList.tournamentList[i].judgeList.Count; judges++)
                            {
                                if (IPList.ipList.Count > 0)
                                {
                                    string s = IPList.findUsersIP(TournamentList.tournamentList[i].judgeList[judges].username);
                                    Transmit("Server#Round#" + TournamentList.tournamentList[i].title + "#" + round, s);
                                }
                                counter = 0;
                                for (int divers = 0; divers < TournamentList.tournamentList[i].diverList.Count; divers++)
                                {
                                    
                                    if (TournamentList.tournamentList[i].diverList[divers].dives[round].diveScore.Count > 0 && TournamentList.tournamentList[i].diverList[divers].dives[round].diveScore.Count <= 6)
                                    {
                                        for (int divecount = 0; divecount < TournamentList.tournamentList[i].diverList[divers].dives[round].diveScore.Count; divecount++)
                                        {
                                            //Console.WriteLine("Counter = " + counter + ", Tournament = " + i + ", Rounds = " + round + ", judges = " + judges + ", divers = " + divers + ", divecount = " + divecount);
                                            if (TournamentList.tournamentList[i].diverList[divers].dives[round].diveScore[divecount].scoreOfJump >= 0)
                                            {
                                                counter++;
                                            }
                                            else
                                            {
                                                string ip = IPList.findUsersIP(TournamentList.tournamentList[i].diverList[divers].dives[round].diveScore[judges].usernameOfJudge);
                                                Transmit("Server#RequiringJudgement#" + TournamentList.tournamentList[i].diverList[divers].username, ip);
                                            }
                                        }
                                    }
                                }
                            }
                            if (counter == (TournamentList.tournamentList[i].diverList.Count * TournamentList.tournamentList[i].judgeList.Count))
                            {
                                round++;
                                if (round > 5)
                                {
                                    TournamentList.tournamentList[i].isStarted = false;
                                    TournamentList.tournamentList[i].isFinished = true;
                                    TournamentList.SerializeToXML();
                                    sendContestInformationBroadcast();
                                    Program.mtp.KillThread(TournamentList.tournamentList[i].title);
                                    return;
                                }
                                nextRound = true;
                            }
                        Thread.Sleep(1000);
                    }
                }
            }
        }
    }
}
